/************************************************************************

								Heroes IV
					  Copyright 2001, The 3DO Company

	------------------------------------------------------------------
  						       caravan.cpp

	$Header: $

	$NoKeywords: $

 ************************************************************************/

// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "caravan.h"

#include "adventure_map.h"
#include "adventure_object_global_id.h"
#include "adventure_object.h"
#include "map_point.h"
#include "owned_adv_object.h"
#include "stationary_adventure_object.h"
#include "town.h"

namespace
{
	t_uint16 const k_current_save_version = 2;
}

// -------------------------------------------------------------
// -------------------------------------------------------------
t_caravan::t_caravan( t_adventure_map * map, int arrival_day, t_adventure_object * origin, t_town_ptr destination, int owner )
	: m_arrival_day( arrival_day ),
	  m_destination( destination ),
	  m_map( map ),
	  m_origin_name( origin ? origin->get_name() : "" ),
	  m_owner( owner )
{
}




// -------------------------------------------------------------
// Attempt -- rather forcefully -- to get the caravan creatures
// into the destination array. Returns true if successful 
// (the caravan was emptied)
// -------------------------------------------------------------
bool t_caravan::add_caravan_to_army( t_creature_array & destination )
{
	int               i;
	int               slot_number;
	t_creature_array & caravan = *this;
	int				  pass;

	for ( pass = 0; pass < 2 && !caravan.empty(); pass++ )
	{
		for (i = 0; i < k_size; i++)
		{
			if (caravan[i].get_number() == 0)
				continue;

			// find an open slot.
			if (destination[i].can_add( caravan[i] ) )
			{
				slot_number = i;
			}
			else
			{
				slot_number = 0;
				while ( slot_number < k_size && !destination[slot_number].can_add( caravan[i] ) )
					slot_number++;

				if (slot_number == k_size)
					continue;
			}
		
			t_creature_stack_ptr stack = new t_creature_stack;
			caravan.swap( stack, i ); // Move out of caravan
			destination.add( stack, slot_number ); // Move into destination
		}

		if ( caravan.empty() )
			return true;

		if ( pass == 0 )
		{
			// If first pass failed, consolidate destination army & try again
			destination.consolidate();
		}
	}

	return false;
}

// -------------------------------------------------------------
// Current save file format version. Static function -- all 
// caravans have the same version
// -------------------------------------------------------------
t_uint16 t_caravan::get_caravan_version()
{
	return k_current_save_version;
}

// -------------------------------------------------------------
// Return map pointer. Override for t_adv_object_map_info
// -------------------------------------------------------------
t_adventure_map * t_caravan::get_map() const
{
	return m_map;
}

// -------------------------------------------------------------
// Has the caravan arrived at its destination?
// -------------------------------------------------------------
bool t_caravan::has_arrived() const
{
	return ( m_arrival_day < m_map->get_day() ||
		     ( m_arrival_day == m_map->get_day() && m_owner <= m_map->get_current_player_number() ) );
}

// -------------------------------------------------------------
// Process a new day 
// -------------------------------------------------------------
void t_caravan::process_new_day()
{
	t_creature_array::process_new_day( false );
}

// -------------------------------------------------------------
// Read from save file
// -------------------------------------------------------------
bool t_caravan::read( std::streambuf & stream, t_uint16 caravan_version )
{
	if ( caravan_version > k_current_save_version )
		return false;

	if ( caravan_version < 2)
	{
		if ( !t_creature_array::read_version( stream, 1 ))
			return false;
	}
	else if (!t_creature_array::read( stream )) // version 2+ gives creature array its own version
		return false;

	m_arrival_day = get< int >( stream );
	m_owner = get< t_uint8 >( stream );

	if ( caravan_version == 0 )
	{
		// Back then, the only way to record a town is by location of its trigger cells [yuck]
		t_level_map_point_2d position;
		position.row = ::get< int >( stream );
		position.column = ::get< int >( stream );
		position.level = ::get< t_uint8 >( stream );

		// This should be called after all objects are on map, so we can resolve the town
		// pointers right away
		t_adventure_tile const *tile = &((*m_map)[position]);

		// Note: towns are never on bridges
		const std::vector<int>& triggers = tile->get_triggers(false);
		for (std::vector<int>::const_iterator iter = triggers.begin(); m_destination.get() == NULL && iter != triggers.end(); iter++)
		{
			t_abstract_adv_object &object = m_map->get_object(*iter);

			t_town * town = dynamic_cast<t_town *>(&object);
			
			if (town)
			{
				m_destination = town;
			}
		}

		if ( m_destination.get() == NULL )
			return false;

		t_adventure_object * origin = NULL;

		position.row = ::get< int >( stream );
		position.column = ::get< int >( stream );
		position.level = ::get< t_uint8 >( stream );

		tile = &((*m_map)[position]);

		// Prefer towns to anything else. Prefer owned objects to non-owned. Must be a stationary object
		bool found_town = false;
		bool found_owned = false;

		for (std::vector<int>::const_iterator iter = triggers.begin(); !found_town && iter != triggers.end(); iter++)
		{
			t_abstract_adv_object &object = m_map->get_object(*iter);

			t_town * town = dynamic_cast<t_town *>(&object);
			
			if (town)
			{
				origin = town;
				found_town = true;
			}
			else if ( !found_owned )
			{
				t_owned_adv_object * owned = dynamic_cast< t_owned_adv_object * >( &object );

				if ( owned )
				{
					origin = owned;
					found_owned = true;
				}
				else if ( origin == NULL )
				{
					t_stationary_adventure_object * stationary = dynamic_cast< t_stationary_adventure_object * >( &object );

					if ( stationary )
					{
						origin = stationary;
					}
				}
			}
		}

		if ( origin == NULL )
			return false;

		m_origin_name = origin->get_name();
	}
	else
	{
		// New method is global object ids
		t_adventure_global_id destination_id = read_adventure_global_id( stream );

		m_destination = dynamic_cast< t_town * >( m_map->get_object_by_global_id( destination_id ) );

		if ( m_destination.get() == NULL )
			return false;

		if ( !read_string16( stream, m_origin_name ) )
			return false;
	}

	return true;
}


// -------------------------------------------------------------
// Write to save file
// -------------------------------------------------------------
bool t_caravan::write( std::streambuf & stream ) const
{
	if ( !t_creature_array::write( stream ) )
		return false;

	put< int >( stream, m_arrival_day );
	put< t_uint8 >( stream, m_owner );

	write_adventure_global_id( stream, m_destination->get_global_id() );

	if ( !write_string16( stream, m_origin_name ) )
		return false;

	return true;
}
